"""
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    created by lei.chang on '24/05/2023'
    comment: 服务流程逻辑
"""
import copy
from datetime import datetime
from datetime import timedelta
from itertools import groupby
from sqlalchemy import func

from config import Config
from project.core.base import BodyParams, Select
from project.core.function import if_none
from project.core.exception import BizError
from project.lib.decorator import with_async_redis
from project.model.database.basic import Component
from project.model.database.cariabot import Cariabot, CariabotColumn, CariabotColumnService
from project.model.database.flow import Flow, FlowRole, FlowProcess, FlowPage
from project.model.database.node import Node, NodeGroup, NodeItem
from project.model.basic import Operator, Dates, Device, ProcessType
from project.service.common.code_generator import CodeGenerator
from project.micro_service.apollo import Apollo
from project.middleware.homecare import HomeCare


class FlowService:

    @staticmethod
    @with_async_redis()
    async def do_flow_add(session, operator: Operator, body_params: BodyParams, rds=None) -> Flow:
        """ 添加服务流程
        :param session:
        :param operator:
        :param body_params:
        :param rds
        :return:
        """
        flow_name = body_params.pop('name')
        if not flow_name:
            index = await rds.incr(f"{Config.PROJECT_NAME}:flow_name_index")
            flow_name = f'未命名流程({str(index)})'

        flow_code = await CodeGenerator.generate_flow_code()
        flow = await Flow.create_modify(session,
                                        code=flow_code,
                                        name=flow_name,
                                        organization_code=operator.organization_code,
                                        **body_params.dict(),
                                        commit=False)

        # 创建用户角色
        await FlowRole.create_modify(session, role_type='user', name='用户', flow_id=flow.id, flow_code=flow.code, remark='固定用户角色', commit=False)

        await session.commit()

        return flow

    @staticmethod
    @with_async_redis()
    async def do_copy_flow(session, operator: Operator, flow_code, template_name, mode='make_template', rds=None) -> Flow:
        """ 复制服务流程
        :param session:
        :param operator:
        :param flow_code:
        :param template_name:
        :param mode: make_template(制作模版本)/use_template(使用模版)
        :param rds
        :return:
        """
        flow = await Flow.fetch_one(session, wheres=(Flow.code == flow_code))
        if not flow:
            raise BizError('服务流程不存在')
        if flow.is_template and mode == 'make_template':
            raise BizError('引入模版时，不可选择模版')
        if not flow.is_template and mode == 'use_template':
            raise BizError('使用模版时，不可使用流程')

        if mode == 'make_template':
            _flow = await Flow.fetch_one(session, wheres=(Flow.name == template_name, Flow.is_template, ~Flow.is_deleted))
            if _flow:
                raise BizError('模版名称已存在，请重新命名')

        new_flow = await Flow.create_modify(session, **flow.dict('id', 'code', reverse=True), commit=False)
        if not new_flow:
            raise BizError('创建新流程失败')
        if mode == 'make_template':
            new_flow.name = template_name
        else:
            index = await rds.incr(f"{Config.PROJECT_NAME}:flow_name_index")
            new_flow.name = f'未命名流程({str(index)})'
        new_flow.code = await CodeGenerator.generate_flow_code()
        new_flow.is_template = 1 if mode == 'make_template' else 0
        if mode == 'use_template':
            new_flow.is_fixed = 0
        # 默认禁用模版
        if new_flow.is_template:
            new_flow.is_forbidden = 1
            new_flow.organization_code = ''
        else:
            new_flow.organization_code = operator.organization_code

        # 复制节点数据
        nodes = await Node.fetch_all(session, wheres=(Node.flow_code == flow_code, ~Node.is_deleted))
        new_nodes = []
        node_id_map = {}
        node_code_map = {}
        for node in nodes:
            _node = await Node.create_modify(session, **node.dict('id', 'code', reverse=True), commit=False)
            _node.code = str(_node.id).zfill(6)
            _node.flow_id = new_flow.id
            _node.flow_code = new_flow.code
            node_id_map[node.id] = _node.id
            node_code_map[node.code] = _node.code
            new_nodes.append(_node)

            # 复制分组数据
            group_id_map = {}
            node_groups = await NodeGroup.fetch_all(session, wheres=(NodeGroup.node_id == node.id, ~NodeGroup.is_deleted), orders=(NodeGroup.id,))
            for node_group in node_groups:
                _node_group = await NodeGroup.create_modify(session, **node_group.dict('id', reverse=True), commit=False)
                _node_group.node_id = _node.id
                group_id_map[node_group.id] = _node_group.id

            # 复制节点项目数据
            node_items = await NodeItem.fetch_all(session, wheres=(NodeItem.node_id == node.id, ~NodeItem.is_deleted), orders=(NodeItem.id,))
            for node_item in node_items:
                _node_item = await NodeItem.create_modify(session, **node_item.dict('id', reverse=True), commit=False)
                _node_item.node_id = _node.id
                _node_item.group_id = group_id_map.get(_node_item.group_id, _node_item.group_id)

        # 复制流程角色
        flow_roles = await FlowRole.fetch_all(session, wheres=(FlowRole.flow_id == flow.id, ~FlowRole.is_deleted), orders=(FlowRole.id,))
        flow_role_id_map = {}
        for flow_role in flow_roles:
            _flow_role = await FlowRole.create_modify(session, **flow_role.dict('id', reverse=True), commit=False)
            _flow_role.flow_id = new_flow.id
            _flow_role.flow_code = new_flow.code

            flow_role_id_map[flow_role.id] = _flow_role.id

        # 复制服务流程工单状态
        flow_processes = await FlowProcess.fetch_all(session, wheres=(FlowProcess.flow_id == flow.id, ~FlowProcess.is_deleted), orders=(FlowProcess.id,))
        flow_process_id_map = {}
        for flow_process in flow_processes:
            _flow_process = await FlowProcess.create_modify(session, **flow_process.dict('id', reverse=True), commit=False)
            _flow_process.flow_id = new_flow.id
            _flow_process.flow_code = new_flow.code

            flow_process_id_map[flow_process.id] = _flow_process.id

        # 复制自定义页面
        flow_pages = await FlowPage.fetch_all(session, wheres=(FlowPage.flow_id == flow.id, ~FlowPage.is_deleted), orders=(FlowPage.id,))
        for flow_page in flow_pages:
            _flow_page = await FlowPage.create_modify(session, **flow_page.dict('id', reverse=True), commit=False)
            _flow_page.flow_id = new_flow.id
            _flow_page.flow_code = new_flow.code
            _flow_page.flow_role_id = flow_role_id_map.get(flow_page.flow_role_id, flow_page.flow_role_id)
            _flow_page.flow_process_id = flow_process_id_map.get(flow_page.flow_process_id, flow_page.flow_process_id)
            _flow_page.to_flow_process_id = flow_process_id_map.get(flow_page.to_flow_process_id, flow_page.to_flow_process_id)

        # 更新下一节点信息
        for new_node in new_nodes:
            new_node.next_node_id = node_id_map.get(new_node.next_node_id, new_node.next_node_id)

        # 更新sop信息
        sop = copy.deepcopy(new_flow.sop)
        for v in sop:
            v['node_code'] = node_code_map.get(v['node_code'], v['node_code'])
        new_flow.sop = sop

        # 更新短信配置信息
        if new_flow.inform_config:
            inform_config = copy.deepcopy(new_flow.inform_config)
            for v in inform_config:
                bind_node_id = v.get('bind_node_id', 0)
                v['bind_node_id'] = node_id_map.get(bind_node_id, bind_node_id)
                flow_role_id = v.get('flow_role_id', 0)
                v['flow_role_id'] = flow_role_id_map.get(flow_role_id, flow_role_id)
                flow_process_id = v.get('flow_process_id', 0)
                v['flow_process_id'] = flow_process_id_map.get(flow_process_id, flow_process_id)
            new_flow.inform_config = inform_config

        await session.commit()

        return new_flow

    @staticmethod
    async def do_role_add(session, body_params: BodyParams):
        """ 添加流程角色
        :param session:
        :param body_params:
        :return:
        """
        flow = await Flow.fetch_one(session, wheres=(Flow.id == body_params.flow_id))
        if not flow:
            raise BizError('服务流程不存在')
        # 判断角色名称是否已存在
        role = await FlowRole.fetch_one(session, wheres=(FlowRole.flow_id == body_params.flow_id, FlowRole.name == body_params.name, ~FlowRole.is_deleted))
        if role:
            raise BizError('角色已存在')
        # 判断手机号是否已被绑定
        role = await FlowRole.fetch_one(session, wheres=(FlowRole.flow_id == body_params.flow_id, FlowRole.phone == body_params.phone, ~FlowRole.is_deleted))
        if role:
            raise BizError('该手机号已绑定角色')
        # 创建用户角色
        role = await FlowRole.create_modify(session, flow_code=flow.code, role_type='worker', **body_params.dict())

        return role

    @staticmethod
    async def do_delete_role(session, flow_role_id):
        """ 删除流程角色
        :param session:
        :param flow_role_id:
        :return:
        """
        flow_role = await FlowRole.fetch_one(session, wheres=(FlowRole.id == flow_role_id))
        if not flow_role:
            raise BizError('流程角色不存在')
        flow_role.is_deleted = True

        # 将对应的自定义页面删除
        flow_pages = await FlowPage.fetch_all(session, wheres=(FlowPage.flow_role_id == flow_role_id, ~FlowPage.is_deleted))
        for flow_page in flow_pages:
            flow_page.is_deleted = True

        # 将对应的短信配置删除
        flows = await Flow.fetch_all(session, wheres=(Flow.inform_config.like(f'%"flow_role_id": {flow_role_id}%'), ~Flow.is_deleted))
        for flow in flows:
            inform_config = [v for v in flow.inform_config if v['flow_role_id'] != flow_role_id]
            flow.inform_config = inform_config

        await session.commit()

    @staticmethod
    async def do_process_add(session, body_params: BodyParams):
        """ 新增服务流程状态
        :param session:
        :param body_params:
        :return:
        """
        flow = await Flow.fetch_one(session, wheres=(Flow.id == body_params.flow_id))
        if not flow:
            raise BizError('服务流程不存在')
        # 判断状态名称是否已存在
        process = await FlowProcess.fetch_one(session, wheres=(FlowProcess.flow_id == body_params.flow_id, FlowProcess.name == body_params.name, ~FlowProcess.is_deleted))
        if process:
            raise BizError('状态已存在')

        # 检查名称是否在 ProcessType 枚举中，并在找到时获取枚举的值
        process_type_map = {member.value.value: member.value.code for member in ProcessType}
        body_params.rank = process_type_map.get(body_params.name)
        # 如果 rank 仍然为空，则查询当前流程状态的最大 rank 值
        if not body_params.rank:
            record = await FlowProcess.fetch_one(session, selects=(func.max(FlowProcess.rank)), wheres=(FlowProcess.flow_id == body_params.flow_id, ~FlowProcess.is_deleted))
            max_rank = if_none(record[0], -1)
            body_params.rank = max_rank + 1

        process = await FlowProcess.create_modify(session, flow_code=flow.code, **body_params.dict())

        # 为每个新增的状态添加默认组件
        component = await Component.fetch_one(session, wheres=(Component.name == '服务详情'))
        if component:
            roles = await FlowRole.fetch_all(session, wheres=(FlowRole.flow_id == body_params.flow_id, ~FlowRole.is_deleted))
            for role in roles:
                await FlowPage.create_modify(session, flow_id=flow.id, flow_code=flow.code, page_type='work_order', flow_role_id=role.id, component_id=component.id, flow_process_id=process.id)

        return process

    @staticmethod
    async def do_process_update(session, body_params: BodyParams):
        """ 更新服务流程状态
        :param session:
        :param body_params:
        :return:
        """
        process = await FlowProcess.fetch_one(session, wheres=(FlowProcess.id == body_params.id, ~FlowProcess.is_deleted))
        if not process:
            raise BizError('流程状态不存在')
        # 如果状态名称未改变，则不需要检查名称是否已存在
        if body_params.name != process.name:
            process_exists = await FlowProcess.fetch_one(session, wheres=(FlowProcess.flow_id == process.flow_id, FlowProcess.name == body_params.name, ~FlowProcess.is_deleted))
            if process_exists:
                raise BizError('状态已存在')

        # 检查名称是否在 ProcessType 枚举中，并在找到时获取枚举的值
        process_type_map = {member.value.value: member.value.code for member in ProcessType}
        body_params.rank = process_type_map.get(body_params.name, process.rank)

        # 更新流程状态
        await FlowProcess.create_modify(session, wheres=(FlowProcess.id == body_params.id), **body_params.dict(), no_record_error=True)

    @staticmethod
    async def do_process_delete(session, flow_process_id):
        """
        :param session:
        :param flow_process_id:
        :return:
        """
        flow_process = await FlowProcess.fetch_one(session, wheres=(FlowProcess.id == flow_process_id))
        if not flow_process:
            raise BizError('工单状态不存在')
        flow_process.is_deleted = True

        # 将对应的自定义页面删除
        flow_pages = await FlowPage.fetch_all(session, wheres=(FlowPage.flow_process_id == flow_process_id, ~FlowPage.is_deleted))
        for flow_page in flow_pages:
            flow_page.is_deleted = True

        # 将自定义页面流转到该状态的清空
        flow_pages = await FlowPage.fetch_all(session, wheres=(FlowPage.to_flow_process_id == flow_process_id, ~FlowPage.is_deleted))
        for flow_page in flow_pages:
            flow_page.to_flow_process_id = 0

        # 将对应的短信配置删除
        flows = await Flow.fetch_all(session, wheres=(Flow.inform_config.like(f'%"flow_process_id": {flow_process_id}%'), ~Flow.is_deleted))
        for flow in flows:
            inform_config = [v for v in flow.inform_config if v['flow_process_id'] != flow_process_id]
            flow.inform_config = inform_config

        await session.commit()

    @staticmethod
    async def do_page_add(session, body_params: BodyParams):
        """ 添加角色页面
        :param session:
        :param body_params:
        :return:
        """
        flow_role = await FlowRole.fetch_one(session, wheres=(FlowRole.id == body_params.flow_role_id, ~FlowRole.is_deleted))
        if not flow_role:
            raise BizError('流程角色不存在')

        flow_process = await FlowProcess.fetch_one(session, wheres=(FlowProcess.id == body_params.flow_process_id, ~FlowProcess.is_deleted))
        if not flow_process:
            raise BizError('流程状态不存在')

        record = await FlowPage.fetch_one(session, selects=(func.max(FlowPage.rank)), wheres=(FlowPage.flow_role_id == body_params.flow_role_id, FlowPage.flow_process_id == body_params.flow_process_id, ~FlowPage.is_deleted))
        max_rank = if_none(record[0], -1)

        flow_page = await FlowPage.create_modify(session,
                                                 flow_id=flow_role.flow_id,
                                                 flow_code=flow_role.flow_code,
                                                 rank=max_rank + 1,
                                                 **body_params.dict())

        return flow_page

    @staticmethod
    async def do_component_add(session, flow_page_id):
        """ 添加组件
        :param session:
        :param flow_page_id: 流程页面id
        :return:
        """
        # 获取页面信息
        flow_page = await FlowPage.fetch_one(session, wheres=(FlowPage.id == flow_page_id, ~FlowPage.is_deleted))
        if not flow_page:
            raise BizError('流程页面不存在')

        # 判断当前页面是否存在录入组件
        component_list = await Component.fetch_all(session, wheres=(Component.component_type == 'input'))
        component_ids = [v.id for v in component_list]
        component = await Component.fetch_one(session, wheres=(Component.component_type == 'submit'))
        if not component:
            raise BizError('提交组件不存在')
        flow_page_submit = await FlowPage.fetch_one(session,wheres=(FlowPage.flow_role_id == flow_page.flow_role_id, FlowPage.flow_process_id == flow_page.flow_process_id, FlowPage.component_id == component.id))
        flow_page_list = await FlowPage.fetch_all(session, wheres=(FlowPage.flow_role_id == flow_page.flow_role_id, FlowPage.flow_process_id == flow_page.flow_process_id, FlowPage.component_id.in_(component_ids), ~FlowPage.is_deleted))
        if flow_page_list:
            # 当前页面存在录入组件，添加提交组件
            if not flow_page_submit:
                await FlowPage.create_modify(session,
                                             flow_id=flow_page.flow_id,
                                             flow_code=flow_page.flow_code,
                                             flow_role_id=flow_page.flow_role_id, 
                                             page_type=flow_page.page_type,
                                             flow_process_id=flow_page.flow_process_id,
                                             component_id=component.id,
                                             rank=-1)
        else:
            # 当前页面不存在录入组件，清除提交组件
            if flow_page_submit:
                flow_page_submit.is_deleted = True
                await session.commit()

    @staticmethod
    async def get_scheduling_day_desc(flow_code):
        """ 得到排班日期列表(agent描述)
        :param flow_code:
        :return:
        """
        # 取得日期排班信息
        start_time = datetime.now()
        end_time = start_time + timedelta(days=30)
        months = set()
        months.add(start_time.strftime('%Y-%m'))
        months.add(end_time.strftime('%Y-%m'))
        dates = []
        for month in months:
            _dates = await HomeCare.get_scheduling_day(Config.NURSING_DEVICE_ID, month, Config.NURSING_SERVICE_MAP.get(flow_code))
            dates.extend([v['date'] for v in _dates if v['status'] == 1])
        dates.sort()  # 按照日期排序

        start_date = end_date = ''
        exclude_dates = []
        for index, date in enumerate(dates):
            if date < datetime.now().strftime('%Y-%m-%d') or date > end_time.strftime('%Y-%m-%d'):
                continue

            if index == 0:
                start_date = date
            else:
                days = (datetime.strptime(date, '%Y-%m-%d') - datetime.strptime(dates[index - 1], '%Y-%m-%d')).days
                if days > 1:
                    for i in range(days):
                        exclude_dates.append((datetime.strptime(dates[index - 1], '%Y-%m-%d') + timedelta(days=i+1)).strftime('%Y-%m-%d'))

            end_date = date

        text = f'{start_date}至{end_date}期间内可选。'
        if exclude_dates:
            text += '但' + '、'.join(exclude_dates) + '不可选'

        return text

    @staticmethod
    async def get_scheduling_time_desc(flow_code, meal_periods):
        """ 得到排班时段列表(agent描述)
        :param flow_code:
        :param meal_periods: 时间段
        :return:
        """
        now = datetime.now()
        start_day = now.strftime('%Y-%m-%d')
        end_day = (now + timedelta(days=30)).strftime('%Y-%m-%d')

        result = await HomeCare.get_scheduling_time_period(Config.NURSING_DEVICE_ID, start_day, end_day, Config.NURSING_SERVICE_MAP.get(flow_code))
        items = [v for v in result if v['number'] == 0 and v['time'][-5:] >= now.strftime('%H:%M')]

        reducible = '、'.join([v['title'] for v in meal_periods])
        irreducible = '、'.join([f'{item["date"]}日期的{item["time"]}' for item in items])

        text = f'每天的{reducible}时段可选。'
        if irreducible:
            text += f'{irreducible}时段不可选。'

        return text

    @staticmethod
    async def get_flow_parameters(session, flow_code):
        """ 取得流程的参数信息
        """
        nodes = await Node.fetch_all(session, wheres=(Node.flow_code == flow_code, ~Node.is_deleted))
        node_ids = [v.id for v in nodes]

        # 节点的分组信息
        node_groups = await NodeGroup.fetch_all(session, wheres=(NodeGroup.node_id.in_(node_ids), ~NodeGroup.is_deleted), orders=(NodeGroup.node_id, NodeGroup.id))
        node_group_map = {key: list(group) for key, group in groupby(node_groups, key=lambda x: x.node_id)}

        # 节点的商品信息
        node_items = await NodeItem.fetch_all(session, wheres=(NodeItem.node_id.in_(node_ids), ~NodeItem.is_deleted), orders=(NodeItem.node_id, NodeItem.group_id, NodeItem.id))
        node_item_map = {key: list(group) for key, group in groupby(node_items, key=lambda x: x.node_id)}

        params = list()
        for node in nodes:
            if not node.name:
                continue
            if node.node_type in ('confirm', 'success'):
                continue
            param = dict()
            param['参数名称'] = node.name
            param['参数描述'] = node.introduction

            node_items = node_item_map.get(node.id)
            if not node_items:
                param['参数值类型'] = '单项'
            else:
                param['参数值类型'] = '列表'

            node_groups = node_group_map.get(node.id)
            if node_groups:
                details = list()
                for node_group in node_groups:
                    detail = dict()
                    detail['分类名'] = node_group.title

                    # 时间段信息
                    meal_period = node_group.meal_period
                    if meal_period:
                        detail['有效时间'] = f'{meal_period["start_time"]}-{meal_period["end_time"]}'

                    node_item_group_map = {key: list(group) for key, group in groupby(node_items, key=lambda x: x.group_id)}
                    _node_items = node_item_group_map.get(node_group.id, [])

                    items = []
                    for _node_item in _node_items:
                        item = dict()
                        item['名称'] = _node_item.title

                        if _node_item.is_spec:
                            spec = dict()
                            for v in _node_item.spec:
                                spec[v.get('title')] = {
                                    '可选项': v.get('items'),
                                    '当前选中': '无'
                                }
                            item['规格'] = spec

                        # 是否可选数量
                        if _node_item.is_count:
                            item['数量'] = {'上限': _node_item.count_upper_limit, '当前选中': 0}

                        # 价格
                        if _node_item.price:
                            item['单价'] = _node_item.price

                        items.append(item)

                    detail['可选列表'] = items
                    details.append(detail)
                param['参数详情'] = details
            else:
                detail = None
                # 时间段选择
                if node.node_type == 'time_period_picker':
                    detail = dict()
                    # 是否有时间配置
                    timeset = node.timeset
                    dates = timeset.get('dates')
                    if dates:
                        date_texts = [Dates.get_value(v) for v in dates]
                        detail['日期'] = {'可选项': date_texts, '当前选中': '无'}

                    meal_period = timeset.get('meal_period')
                    if meal_period:
                        detail['时间'] = {'可选项': f'只能选择{meal_period["start_time"]}-{meal_period["end_time"]}时间内', '格式': '时:分, 比如19:00', '当前选中': ""}
                # 预约服务时间
                elif node.node_type == 'service_time_booking' and Config.NURSING_SERVICE_MAP.get(flow_code):
                    detail = dict()
                    text = await FlowService.get_scheduling_day_desc(flow_code)
                    detail['日期'] = {'可选项': text, '当前选中': '无'}

                    if node.meal_periods:
                        text = await FlowService.get_scheduling_time_desc(flow_code, node.meal_periods)
                        detail['时段'] = {'可选项': text, '当前选中': '无'}

                    if node.time_durations:
                        detail['时长'] = {'可选项': node.time_durations, '当前选中': '无'}

                # 手机号输入
                elif node.node_type == 'text_phone':
                    detail = dict()
                    # 11位
                    if node.phone_type == 'eleven_bit':
                        detail[node.name] = {
                            '可选项': '11位手机号',
                            '格式': '11位数字, 比如13520579960',
                            '当前选中': ""
                        }
                    # 4位手机尾号
                    elif node.phone_type == 'last_four_bit':
                        detail[node.name] = {
                            '可选项': '4位手机尾号',
                            '格式': '4位数字, 比如9960',
                            '当前选中': ""
                        }
                # 图文列表、文本列表
                elif node.node_type in ('image_display_select', 'text_picker'):
                    detail = list()
                    for _node_item in node_items:
                        item = dict()
                        item['名称'] = _node_item.title
                        # 是否可选规则
                        if _node_item.is_spec:
                            spec = dict()
                            for v in _node_item.spec:
                                spec[v.get('title')] = {
                                    '可选项': v.get('items'),
                                    '当前选中': '无'
                                }
                            item['规格'] = spec

                        # 是否可选数量
                        if _node_item.is_count:
                            item['数量'] = {'上限': _node_item.count_upper_limit, '当前选中': 0}

                        # 价格
                        if _node_item.price:
                            item['单价'] = _node_item.price

                        detail.append(item)

                param['参数详情'] = detail

            params.append(param)

        return params

    @staticmethod
    async def get_flow_agent_info(session, flow_id=0, flow_code=''):
        """ 得到服务流程的agent信息
        :param session:
        :param flow_id:
        :param flow_code:
        :return:
        """
        if flow_id:
            wheres = (Flow.id == flow_id)
        elif flow_code:
            wheres = (Flow.code == flow_code)
        else:
            return {}
        flow = await Flow.fetch_one(session, wheres=wheres)
        if not flow:
            return {}

        ret_data = flow.dict('code', 'title', 'description', 'matching_intent', 'name', 'introduction')
        sop_texts = []
        for idx, v in enumerate(flow.sop):
            node = await Node.fetch_one(session, wheres=(Node.code == v['node_code'], ~Node.is_deleted))
            if not node:
                continue
            if node.flow_code != flow.code:
                continue
            text = f'Step{str(idx+1)}):{v["content"]} 对应节点: "{v["node_code"]}"'

            if '${day_periods}' in text:
                day_periods = await FlowService.get_scheduling_day_desc(flow.code)
                text = text.replace('${day_periods}', day_periods)
            if '${time_periods}' in text:
                time_periods = await FlowService.get_scheduling_time_desc(flow.code, node.meal_periods)
                text = text.replace('${time_periods}', time_periods)
            if '${time_durations}' in text:
                text = text.replace('${time_durations}', '、'.join(node.time_durations))

            sop_texts.append(text)
        ret_data['sop'] = '\n'.join(sop_texts)
        ret_data['parameters'] = await FlowService.get_flow_parameters(session, flow.code)
        ret_data['title'] = ret_data['name']
        ret_data['description'] = ret_data['introduction']

        return ret_data

    @staticmethod
    async def do_flow_interaction(session, flow_id, user_message, operator, is_init):
        """ 服务流程交互
        :param session:
        :param flow_id:
        :param user_message:
        :param operator:
        :param is_init:
        :return:
        """
        flow = await FlowService.get_flow_agent_info(session, flow_id=flow_id)
        if not flow:
            raise BizError('服务流程不存在')

        result = await Apollo.do_flow_interaction(operator, user_message, flow, is_init)

        return result

    @staticmethod
    async def get_cariabot_flow_list(session, device: Device, sidebar_id=0) -> list[Flow]:
        """ 取得cariabot的服务流程
        :param session:
        :param device:
        :param sidebar_id:
        :return:
        """
        wheres = list()
        if sidebar_id:
            wheres.append(CariabotColumnService.column_id == sidebar_id)
        else:
            cariabot = await Cariabot.fetch_one(session, wheres=(Cariabot.organization_code == device.organization_code, ~Cariabot.is_deleted))
            if not cariabot:
                return []
            cariabot_columns = await CariabotColumn.fetch_all(session,
                                                              wheres=(CariabotColumn.cariabot_id == cariabot.id, ~CariabotColumn.is_deleted, CariabotColumn.column_type == 'flow'))
            column_ids = [v.id for v in cariabot_columns]
            wheres.append(CariabotColumnService.column_id.in_(column_ids))
        wheres.append(~CariabotColumnService.is_deleted)

        data = await Select(
            selects=(Flow, CariabotColumnService),
            joins=((CariabotColumnService, Flow.code == CariabotColumnService.code),),
            wheres=wheres,
            orders=(CariabotColumnService.id,)
        ).fetch_all(session)

        flows = list()
        for flow, cariabot_service in data:
            flow.image = cariabot_service.image

            flows.append(flow)

        return flows


